package com.cysyz.mylib.utils;

import com.cysyz.mylib.utils.myassert.MyAssert;
import org.springframework.util.ReflectionUtils;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.*;

public class MyReflectionUtils extends ReflectionUtils{
    public static Class getActualType(Class clazz, int index){
        ParameterizedType genericSuperclass = (ParameterizedType) clazz.getGenericSuperclass();
        return (Class) genericSuperclass.getActualTypeArguments()[index];
    }

    public static <T extends Annotation> T getAnnotation(Field field, Class<T> clazz) {
        MyAssert.notNullMulti(field, clazz);
        T retVal = null;
        if (field != null && field.isAnnotationPresent(clazz)) {
            retVal = (T) field.getAnnotation(clazz);
        }
        return retVal;
    }

    public static <T extends Annotation> T getAnnotation(Method method, Class<T> clazz) {
        MyAssert.notNullMulti(method, clazz);
        T retVal = null;
        if (method != null && method.isAnnotationPresent(clazz)) {
            retVal = (T) method.getAnnotation(clazz);
        }
        return retVal;
    }

    public static Field getField(Object obj, String fieldName) {

        MyAssert.notNullMulti(obj, fieldName);
        Field field = null;
        Set<Field> declaredFields = MyReflectionUtils.getDeclaredFields(obj);
        for (Field declaredField : declaredFields) {
            if (declaredField.getName().equals(fieldName)) {
                field = declaredField;
                break;
            }
        }
        return field;
    }

    /**
     * 循环向上转型, 获取对象的 DeclaredField
     * @param object : 子类对象
     * @return 父类中的属性对象
     */

    public static Set<Field> getDeclaredFields(Object object){
        Set<Field> fileds = new HashSet<Field>();
        //Field field = null ;

        Class<?> clazz = object.getClass() ;

        while(clazz.getSuperclass() != Object.class) {
            fileds.addAll(Arrays.asList(clazz.getDeclaredFields()));
            clazz = clazz.getSuperclass();
        }
        fileds.addAll(Arrays.asList(clazz.getDeclaredFields()));

        return fileds;
    }

    public static Object getFieldValue(String fieldName, Object bean) {
        Field field = MyReflectionUtils.findField(bean.getClass(), fieldName);
        MyAssert.notNull(field);
        MyReflectionUtils.makeAccessible(field);
        return MyReflectionUtils.getField(field, bean);
    }

    public static void setFieldValue(Object target, String targetFieldName,
                                     Object fieldValue) {
        Field findField = MyReflectionUtils.findField(target.getClass(), targetFieldName);
        MyReflectionUtils.makeAccessible(findField);
        MyReflectionUtils.setField(findField, target, fieldValue);
    }

    public static void printBeanInfo(Object bean, String... fieldNames) {
        System.out.println("打印对象【"+bean.getClass().getName()+"】的信息");
        Field field = null;
        List<Object> justShowTypelist = new ArrayList<Object>();
        justShowTypelist.add(1);
        justShowTypelist.add(1.11);
        justShowTypelist.add(1.11f);
        justShowTypelist.add(1l);
        justShowTypelist.add("");
        for (Object justShowTOjbect : justShowTypelist) {
            if (bean.getClass() == justShowTOjbect.getClass()) {
                System.out.println(bean.toString());
                return;
            }
        }
        if (fieldNames.length == 0) {
            for (Field tField : bean.getClass().getDeclaredFields()) {
                printField(bean, tField);
            }
        } else {

            for (String name : fieldNames) {
                try {
                    field = bean.getClass().getDeclaredField(name);
                } catch (SecurityException e) {
                    e.printStackTrace();
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                }
                printField(bean, field);
            }


        }
        System.out.println();
        System.out.println();
    }

    public static void printBeanInfo(Collection collection) {
        System.out.println("打印对象【"+collection.getClass().getName()+"】的信息");
        for (Object o : collection) {
            printBeanInfo(o);
        }
    }

    public static <E> void printBeanInfo(Collection<E> collection, String... fieldNames) {
        MyAssert.notNull(collection);
        for (Iterator<E> iterator = collection.iterator(); iterator.hasNext();) {
            E e = (E) iterator.next();
            MyReflectionUtils.printBeanInfo(e, fieldNames);
        }
    }

    private static void printField(Object bean, Field field) {
        StringBuffer fieldInfo;
        fieldInfo = new StringBuffer(field.getName());
        fieldInfo.append("[");
        fieldInfo.append(field.getType());
        fieldInfo.append("]");
        fieldInfo.append(":");
        try {
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            fieldInfo.append(field.get(bean));
        } catch (Exception e) {
        }
        System.out.println(fieldInfo);

    }

    public static void copyProperties(Object original, Object target, String... fieldNames) {
        MyAssert.notNullMulti(original, target, fieldNames);
        for (String fieldName : fieldNames) {
            String[] origAndTargetFieldNames = fieldName.split(":");
            if (origAndTargetFieldNames.length == 2 || origAndTargetFieldNames.length == 1) {
                String originalFieldName = null;
                String targetFieldName = null;
                if (origAndTargetFieldNames.length == 2) {
                    originalFieldName = origAndTargetFieldNames[0];
                    targetFieldName = origAndTargetFieldNames[1];
                } else if (origAndTargetFieldNames.length == 1) {
                    originalFieldName = targetFieldName = origAndTargetFieldNames[0];
                }
                Object fieldValue = MyReflectionUtils.getFieldValue(originalFieldName, original);
                setFieldValue(target, targetFieldName, fieldValue);
            } else {
                throw new IllegalArgumentException("fieldNames格式错误，必须是【数据来源对象的属性名称】:【目标对象的属性名称】格式：" + origAndTargetFieldNames);
            }
        }
    }

    /**
     * 获得JavaBean的所有属性值，包括从父类继承的属性
     *
     * @return 一个Map，其中Key为属性名，Value为属性值。
     */
    public static Map<String, Object> getPropValues(Object bean) {
        return getPropValues(bean, getReadablePropNames(bean));
    }

    public static Set<String> getReadablePropNames(Object bean) {
        return BeanClassUtils.getReadablePropNames(bean.getClass());
    }

    private static Map<String, Object> getPropValues(Object bean, Set<String> propNames) {
        Map<String, Object> results = new HashMap<String, Object>();
        Map<String, PropertyDescriptor> props = BeanClassUtils.getPropertyDescriptors(bean.getClass());
        try {
            for (String propName : propNames) {
                PropertyDescriptor propertyDescriptor = props.get(propName);
                Method readMethod = propertyDescriptor.getReadMethod();
                if (readMethod == null) {
                    continue;
                }
                Object value = readMethod.invoke(bean, new Object[]{});
                results.put(propName, value);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return results;
    }

    /**
     * 获得JavaBean的属性值的值，包括从父类继承的属性，不包含指定的属性。
     *
     * @param excludeAnnotations 一批Annotation，被这些Annotation标注的属性将被排除
     * @return 一个Map，其中Key为属性名，Value为属性值。
     */
    public static Map<String, Object> getPropValuesExclude(Object bean, Class<? extends Annotation>... excludeAnnotations) {
        return getPropValues(bean, BeanClassUtils.getReadablePropNamesExclude(bean.getClass(), excludeAnnotations));
    }
}
